﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace SmartChess
{
    public partial class MainWindow : Window, IMoveSelector
    {
        public ObservableCollection<ChessPiece> Pieces
        {
            get;
            set;
        }
        private Border[] GridBoard;       
        private bool IsPieceFocused = false;
        private int FocusedPiece = 0;
        private ChessMove move = new ChessMove();
        Match match = null;
        GameRules gameRules = new GameRules();
        private FIELDCOLOR[] ColorBoard;
        public enum FIELDCOLOR
        {
            WhiteField = 0, // Helles Braun, stellt weißes Feld dar.                                                         
            BlackField = 1, //  Drunkles Braun, stellt schwarzes Feld dar.                                                   
            MarkTargetField = 2,   // Borderfarbe Zielfeld                                                                                
            MarkCurrentField = 3,  //Borderfarbe Ursprungsfeld
            ResetBorder = 4, //Reset auf Standart
            MarkField = 5,  //Färbt Felder grünlich, um legale Züge anzuzeigen                     
        }

        public MainWindow()
        {
            Pieces = new ObservableCollection<ChessPiece>();
            InitializeComponent();
            this.DataContext = Pieces;
            BuildBoard();
        }
        /*
       * Auflistung der Gridfelder und wie die Farben standartmäßig sein müssen.                                                                                       
       * 
       */
        public void BuildBoard()
        {
            GridBoard = new Border[64] { GridField0,  GridField1,  GridField2,  GridField3,  GridField4,  GridField5,  GridField6,  GridField7,
                                      GridField8,  GridField9,  GridField10, GridField11, GridField12, GridField13, GridField14, GridField15,
                                      GridField16, GridField17, GridField18, GridField19, GridField20, GridField21, GridField22, GridField23,
                                      GridField24, GridField25, GridField26, GridField27, GridField28, GridField29, GridField30, GridField31,
                                      GridField32, GridField33, GridField34, GridField35, GridField36, GridField37, GridField38, GridField39,
                                      GridField40, GridField41, GridField42, GridField43, GridField44, GridField45, GridField46, GridField47,
                                      GridField48, GridField49, GridField50, GridField51, GridField52, GridField53, GridField54, GridField55,
                                      GridField56, GridField57, GridField58, GridField59, GridField60, GridField61, GridField62, GridField63};
            ColorBoard = new FIELDCOLOR[64]
                                    {FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,
                                    FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,
                                    FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,
                                    FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,                                            
                                    FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,
                                    FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,
                                    FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,
                                    FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,FIELDCOLOR.BlackField,FIELDCOLOR.WhiteField,};
        }
        /*
         *   Neues Spiel starten.
        */
        private void ButtonNewGame_Click(object sender, RoutedEventArgs e)
        {
            if (match != null)
            {
                ResetBorders();
                match.Moved -= match_Moved;
                match.Player1.Detach(this);
                match.Player2.Detach(this);
                match = null;
            }
            Brain player1, player2;
            switch (CmbPlayer1.SelectedIndex)
            {
                case 0:
                    player1 = new InteractiveBrain();
                    break;
                case 1:
                    player1 = new MiniMaxBrain();
                    (player1 as MiniMaxBrain).depth = cmbDepth.SelectedIndex;
                    (player1 as MiniMaxBrain).algorithmus = cmbAlgorithmus.SelectedIndex;
                    break;
                default:
                    player1 = null;
                    MessageBox.Show("Bitte wählen Sie einen gültigen Spieler aus.");
                    break;
            }
            switch (CmbPlayer2.SelectedIndex)
            {
                case 0:
                    player2 = new InteractiveBrain();
                    break;
                case 1:
                    player2 = new MiniMaxBrain();
                    (player2 as MiniMaxBrain).depth = cmbDepth.SelectedIndex;
                    (player2 as MiniMaxBrain).algorithmus = cmbAlgorithmus.SelectedIndex;
                    break;
                default:
                    player2 = null;
                    MessageBox.Show("Bitte wählen Sie einen gültigen Spieler aus.");
                    break;
            }
            player1.Attach(this);
            player2.Attach(this);
            match = new Match(player1, player2);
            match.Moved += match_Moved;
            match.NextMove();
            statusPlayer.Text = "";
            statusChess.Text = "";
            debug.Clear();
            DisplayMatch();
        }
        //Zeichnet alle Figuren so, wie sie im Board definiert sind.
        private void DisplayMatch()
        {
            Pieces.Clear();
            for (int row = 0; row < 8; row++)
            {
                for (int column = 0; column < 8; column++)
                {
                    if (match.Game.GetUnit(row, column) != 00)
                    {
                        switch (match.Game.GetUnit(row, column))
                        {
                            case Game.UNIT.BBishop:
                                Pieces.Add(new ChessPiece() { Row = row, Column = column, Type = ChessPieceTypes.Bishop, IsBlack = true });
                                break;
                            case Game.UNIT.BKing:
                                Pieces.Add(new ChessPiece() { Row = row, Column = column, Type = ChessPieceTypes.King, IsBlack = true });
                                break;
                            case Game.UNIT.BKnight:
                                Pieces.Add(new ChessPiece() { Row = row, Column = column, Type = ChessPieceTypes.Knight, IsBlack = true });
                                break;
                            case Game.UNIT.BPawn:
                                Pieces.Add(new ChessPiece() { Row = row, Column = column, Type = ChessPieceTypes.Pawn, IsBlack = true });
                                break;
                            case Game.UNIT.BQueen:
                                Pieces.Add(new ChessPiece() { Row = row, Column = column, Type = ChessPieceTypes.Queen, IsBlack = true });
                                break;
                            case Game.UNIT.BRook:
                                Pieces.Add(new ChessPiece() { Row = row, Column = column, Type = ChessPieceTypes.Rook, IsBlack = true });
                                break;
                            case Game.UNIT.WBishop:
                                Pieces.Add(new ChessPiece() { Row = row, Column = column, Type = ChessPieceTypes.Bishop, IsBlack = false });
                                break;
                            case Game.UNIT.WKing:
                                Pieces.Add(new ChessPiece() { Row = row, Column = column, Type = ChessPieceTypes.King, IsBlack = false });
                                break;
                            case Game.UNIT.WKnight:
                                Pieces.Add(new ChessPiece() { Row = row, Column = column, Type = ChessPieceTypes.Knight, IsBlack = false });
                                break;
                            case Game.UNIT.WPawn:
                                Pieces.Add(new ChessPiece() { Row = row, Column = column, Type = ChessPieceTypes.Pawn, IsBlack = false });
                                break;
                            case Game.UNIT.WQueen:
                                Pieces.Add(new ChessPiece() { Row = row, Column = column, Type = ChessPieceTypes.Queen, IsBlack = false });
                                break;
                            case Game.UNIT.WRook:
                                Pieces.Add(new ChessPiece() { Row = row, Column = column, Type = ChessPieceTypes.Rook, IsBlack = false });
                                break;
                            default:
                                break;
                        }
                    }
                }
            }
            
        }      
        /*  
        *   Markiert die Felder des Grids (Visuelles Schachbrett)                                                           
        *   Angegeben wird die Feldnummer von 0-63 und der Enum Wert für die Färbung                                       
        */
        public void MarkField(int FieldID, FIELDCOLOR FIELDCOLOR)
        {
            Color InnerColor = Color.FromArgb(0, 0, 0, 0);
            switch (FIELDCOLOR)
            {
                case FIELDCOLOR.WhiteField:
                    {
                        GridBoard[FieldID].Background = new SolidColorBrush(Color.FromArgb(255, 199, 165, 131));
                        break;
                    }
                case FIELDCOLOR.BlackField:
                    {
                        GridBoard[FieldID].Background = new SolidColorBrush(Color.FromArgb(255, 133, 82, 30));
                        break;
                    }
                case FIELDCOLOR.MarkTargetField:
                    {
                        GridBoard[FieldID].BorderBrush = new SolidColorBrush(Color.FromArgb(255, 66, 40, 14));
                        GridBoard[FieldID].BorderThickness = new Thickness(4, 4, 6, 6);
                        break;
                    }
                case FIELDCOLOR.MarkCurrentField:
                    {
                        GridBoard[FieldID].BorderBrush = new SolidColorBrush(Color.FromArgb(255, 66, 40, 14));
                        GridBoard[FieldID].BorderThickness = new Thickness(4, 4, 6, 6);
                        break;
                    }
                case FIELDCOLOR.ResetBorder:
                    {
                        GridBoard[FieldID].BorderBrush = new SolidColorBrush(Colors.Black);
                        GridBoard[FieldID].BorderThickness = new Thickness(1, 1, 1, 1);
                        break;
                    }
                case FIELDCOLOR.MarkField:
                    {
                        int i = FieldID;
                        if (ColorBoard[i] == FIELDCOLOR.BlackField)
                            GridBoard[FieldID].Background = new SolidColorBrush(Color.FromArgb(255, 182, 245, 150));
                        else
                            GridBoard[FieldID].Background = new SolidColorBrush(Color.FromArgb(255, 191, 235, 164));
                        break;
                    }
                default:
                    {
                        break;
                    }
            }
            InitializeComponent();
        }

        //Bei Click auf eine Figur wird entweder eine vorher ausgewählte Figur auf dieses Feld gesetzt und die hier stehende geschlagen, oder diese Figur wird ausgewählt.
        private void Piece_MouseDown(object sender, MouseEventArgs e)
        {
            var field = sender as UIElement;
            int row = Grid.GetRow(field);
            int column = Grid.GetColumn(field);

            var player = match.CurrentPlayer;
            move.PlayerId = match.GetPlayerId(player);    
        
            if (IsPieceFocused)
            {
                MoveAndCapture(row, column);
            }
            else
            {
                FocusePiece(row, column);            
            }            
        }
        private void MoveAndCapture(int row, int column)
        {
            Recolor();
            IsPieceFocused = false;
            move.TargetColumn = column;
            move.TargetRow = row;
            if (gameRules.IsLegal(move, match.Game))
            {
                if (!match.Game.CopyAndMove(move).IsKingAttacked(match.GetPlayerId(match.CurrentPlayer)))
                    OnMoveSelected(move);                
            }
            else
            {
                FocusePiece(row, column);
            }              
        }
        private void FocusePiece(int row, int column)
        {
            SelectPiece(row, column);
            move.CurrentRow = row;
            move.CurrentColumn = column;
            IsPieceFocused = true;
            CheckForLegalMoves();    
        }
        //Event, welches ausgelößt wird, wenn ein Zug vom Spieler getan wurde.
        //Dadurch bekommt das InteractiveBrain den Zug mit.
        public event MoveSelectEventHandler MoveSelected;
        private void OnMoveSelected(ChessMove move)
        {
            MoveSelected(this, new MoveSelectedEventArgs(move));
        }
        private void Recolor()
        {
            for (int i = 0; i < 64; i++)
            {
                MarkField(i, ColorBoard[i]);
            }
        }
        private void SelectPiece(int row, int column)
        {
            for (int i = 0; i < Pieces.Count; i++)
            {
                if (row == Pieces[i].Row && column == Pieces[i].Column)
                {                    
                    FocusedPiece = i;
                }
            }
        }
        //Prüft alle legalen Züge und makiert sie. 
        private void CheckForLegalMoves()
        {
            for (int r = 0; r < 8; r++)
            {
                for (int c = 0; c < 8; c++)
                {
                    move.TargetRow = r;
                    move.TargetColumn = c;
                    if (gameRules.IsLegal(move, match.Game))
                    {
                        //Es muss eine Kopie von dem Zug gemacht werden, um "in die Zukunft zu schauen" und Züge, die den König in Schach setzten zu verhindern. 
                        if(!match.Game.CopyAndMove(move).IsKingAttacked(match.GetPlayerId(match.CurrentPlayer)))
                            MarkField(((r) * 8 + (c)), FIELDCOLOR.MarkField);
                    }
                }
            }
        }
        
        //Wenn eine Figur ausgewählt ist, wird sie bei Click auf ein Feld dorthin bewegt.
        private void GridField_MouseDown(object sender, MouseEventArgs e)
        {

            if (IsPieceFocused)
            {
                var field = sender as UIElement;
                int row = Grid.GetRow(field) - 1;
                int column = Grid.GetColumn(field) - 1;

                move.TargetColumn = column;
                move.TargetRow = row;
                if (gameRules.IsLegal(move, match.Game))
                {
                    if (!match.Game.CopyAndMove(move).IsKingAttacked(match.GetPlayerId(match.CurrentPlayer)))
                        OnMoveSelected(move);
                }

                IsPieceFocused = false;
                Recolor();
            }
        }
        //Wir jedes mal von Match angestoßen, nachdem ein Zug gemacht wurde.
        async void match_Moved(object sender, GameEventArgs e)
        {
            await Task.Delay(100);            
            MarkLastMove(e.Move);
           
            DisplayMatch();
            SetDebugInfo(e.Move);

            if (!match.Game.GameOver)
                match.NextMove();
            else
                statusChess.Text += "Schachmatt!";
        }
        //Makiert den letzten ausgeführten Zug durch Umrandung der Felder.
        private void MarkLastMove(ChessMove move)
        {
            ResetBorders();
            MarkField(move.CurrentRow * 8 + move.CurrentColumn, FIELDCOLOR.MarkCurrentField);
            MarkField(move.TargetRow * 8 + move.TargetColumn, FIELDCOLOR.MarkTargetField);
        }
        private void ResetBorders()
        {
            for (int i = 0; i < 64; i++)
            {
                MarkField(i, FIELDCOLOR.ResetBorder);
            }
        } 
        private void SetDebugInfo(ChessMove move)
        {
            string farbe = match.GetPlayerId(match.CurrentPlayer) == 0 ? "Weiß" : "Schwarz";
  
            String origin = "ABCDEFGH"[move.CurrentColumn] + (move.CurrentRow + 1).ToString();
            String destination = "ABCDEFGH"[move.TargetColumn] + (move.TargetRow + 1).ToString();
            debug.Text += farbe + ": " + origin + " zu " + destination  + "\n"; 
            debug.ScrollToEnd();

            if (match.Opponent(match.CurrentPlayer).GetType() != typeof(InteractiveBrain))
                statusPlayer.Text = match.Opponent(match.CurrentPlayer).GetTimer();

            if (match.Game.IsKingAttacked(match.GetPlayerId(match.CurrentPlayer)))
                statusChess.Text = match.GetPlayerId(match.CurrentPlayer) == 0 ? "Schwarz steht im Schach" : "Weiß steht im Schach";
            else
                statusChess.Text = "";
        }
        private void CheckMate_Click(object sender, RoutedEventArgs e)
        {
            if (match != null)
            {
                ChessMove move = match.Game.MovesFor(match.GetPlayerId(match.CurrentPlayer)).ElementAt(0);
                if (move.Check)
                    OnMoveSelected(move);
                else
                    statusPlayer.Text = "Du bist noch nicht Schachmatt!";
            }
        }
    }
}
